package com.qijia.travel.helper;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.qijia.travel.common.Constants;

public class FileUploadHelper {

	private int SmallWitdh = 160;
	private int SamllHeight = 120;
	private int BigWitdh = 400;
	private int BigHeight = 300;

	/**
	 * 上传的文件模式,默认为普通模式
	 */
	private String File_mode = Constants.NORML_MODE;

	/**
	 * 允许暂存在内存中的空间大小
	 */
	private Long maxRequestSize = new Long(50 * 1024 * 1024);

	/**
	 * 上传请求request对象
	 */
	private HttpServletRequest request;

	/**
	 * 存储普通的field值
	 */
	private Map<String, Object> feildValueMap = new HashMap<String, Object>();

	/**
	 * 是否需要生成缩略图(在图片模式下有效)
	 */
	private boolean smallFlag = false;

	/**
	 * 是否需要获取普通的field值
	 */
	private boolean isWorked = false;

	/**
	 * 文件存储路径
	 */
	private String saveFileDir;

	/**
	 * 存储至服务器端的文件名
	 */
	private List<String> savedFileName = new ArrayList<String>();

	/**
	 * 允许上传的文件格式
	 */
	private String[] Suffixs = { "jpg", "jpeg", "jpe", "jfif", "gif", "png", "xls", "doc"  };

	private ProgressListener pListener;

	public FileUploadHelper() {
		this.saveFileDir = Constants.FILE_SAVE_DIR;
	}

	/**
	 * Constants.NORML_MODE 普通模式:只上传文件
	 * <p>
	 * Constants.IMG_MODE 图片模式:上传文件 且格式化图片
	 * 
	 * @param mode
	 *            指定的上传文件模式
	 */
	public FileUploadHelper(String mode) {
		this.saveFileDir = Constants.FILE_SAVE_DIR;
		this.File_mode = mode;
	}

	public void setRequest(HttpServletRequest request) {
		this.request = request;
	}

	public void setMaxRequestSize(Long maxRequestSize) {
		this.maxRequestSize = maxRequestSize;
	}

	/**
	 * 指定上传文件的存储路径,路径为相对路径,即相对于工程根目录(/)路径
	 * <p>
	 * e.g 如要存放到根目录下的image文件夹则设置为 image/
	 * 
	 * @param saveFileDir
	 *            上传文件存储路径
	 */
	public void setSaveFileDir(String saveFileDir) {
		this.saveFileDir = saveFileDir;
	}

	public Object getParameterValue(String name) throws Exception {
		if (this.isWorked)
			return feildValueMap.get(name);
		else
			throw new Exception("request还未解析!请确认work()方法执行解析后,再取值!");
	}

	/**
	 * 上传设置,设置结束后返回已设置的ServletFileUpload
	 * 
	 * @return 返回经过设置的ServletFileUpload
	 */
	private ServletFileUpload uploadSetting() {

		// 创建文件处理工厂，它用于生成 FileItem 对象。
		DiskFileItemFactory factory = new DiskFileItemFactory();

		String dir = request.getSession().getServletContext().getRealPath("/");

		java.io.File d = new java.io.File(dir + Constants.TMP_DIR);

		if (!d.exists()) {
			d.mkdirs();
		}
		factory.setSizeThreshold(1024 * 1024); // 设置最多只允许在内存中存储的数据,单位:字节
		factory.setRepository(d); // 设置一旦文件大小超过getSizeThreshold()的值时数据存放在硬盘的目录(默认可以不用设置)

		// Create a new file upload handler
		ServletFileUpload upload = new ServletFileUpload(factory);

		// 设置允许用户上传文件大小,单位:字节
		upload.setSizeMax(maxRequestSize);

		return upload;
	}

	/**
	 * 获取解析后的FileItem
	 * 
	 * @param upload
	 *            ServletFileUpload
	 * @return 返回解析出来的FileItem
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private List<FileItem> getFileItems(ServletFileUpload upload)
			throws Exception {

		if (null != pListener)
			upload.setProgressListener(pListener);

		if (ServletFileUpload.isMultipartContent(request)) {
			List<FileItem> items = null;
			try {
				upload.setHeaderEncoding("UTF-8");

				items = upload.parseRequest(request);

			} catch (FileUploadException e1) {
				throw new Exception(e1);
			}
			return items;
		} else
			throw new Exception("提交的表单不是MultipartContent型表单!");
	}

	/**
	 * 支持单个或多个 不指定文件名 文件的上传
	 * 
	 * @throws Exception
	 *             上传异常抛出Util级别异常
	 */
	public void work() throws Exception {

		// 上传文件,并解析出所有的表单字段，包括普通字段和文件字段
		List<FileItem> items = getFileItems(uploadSetting());

		for (FileItem fileItem : items) {
			if (!fileItem.isFormField()) {// 只对文件字段处理
				// 保存文件，把缓存里的数据写到目标路径下
				if (fileItem.getName() != null && fileItem.getSize() != 0) {
					if (checkFile(fileItem.getName()))
						writeFile(fileItem);
					else
						throw new Exception("请上传正确的文件格式");
				} else
					throw new Exception("没有选择上传文件或上传空文件!");
			} else {
				storeFormFeild(fileItem);
			}
		}

		this.isWorked = true;

	}

	/**
	 * 判断文件的格式
	 * 
	 * @param name
	 * @return
	 */
	private boolean checkFile(String name) {
		if (name == null)
			return false;

		if (name.indexOf(".") <= 0)
			return false;

		String suffix = name
				.substring(name.lastIndexOf(".") + 1, name.length());

		for (String allowSuf : Suffixs) {
			if (allowSuf.equalsIgnoreCase(suffix))
				return true;
		}

		return false;
	}

	/**
	 * 支持单个指定文件名的文件上传
	 * 
	 * @param fileName
	 *            指定的文件名
	 * @throws Exception
	 *             上传异常抛出Util级别异常
	 */
	public void work(String fileName) throws Exception {
		// 上传文件,并解析出所有的表单字段，包括普通字段和文件字段
		List<FileItem> items = getFileItems(uploadSetting());
		for (FileItem fileItem : items) {
			if (!fileItem.isFormField()) {// 只对文件字段处理
				// 保存文件，把缓存里的数据写到目标路径下
				if (fileItem.getName() != null && fileItem.getSize() != 0) {
					if (checkFile(fileItem.getName()))
						writeFile(fileItem, fileName);
					else
						throw new Exception("请上传正确的文件格式");
				} else
					throw new Exception("没有选择上传文件或上传空文件!");
			} else {
				storeFormFeild(fileItem);
			}
		}
		this.isWorked = true;
	}

	@SuppressWarnings("unchecked")
	private void storeFormFeild(FileItem fileItem) {

		if (feildValueMap.containsKey(fileItem.getFieldName())) {
			List<Object> dataList = null;

			Object oldObj = feildValueMap.get(fileItem.getFieldName());

			if (feildValueMap.get(fileItem.getFieldName() + "_list") != null) {
				dataList = (List<Object>) feildValueMap.get(fileItem
						.getFieldName()
						+ "_list");

				if (!dataList.contains(oldObj))
					dataList.add(oldObj);
			} else {
				dataList = new ArrayList<Object>();
				dataList.add(oldObj);
			}

			dataList.add(fileItem.get());
			feildValueMap.put(fileItem.getFieldName() + "_list", dataList);
		} else {
			feildValueMap.put(fileItem.getFieldName(), fileItem.get());
		}
	}

	/**
	 * 支持多个指定文件名的文件上传
	 * 
	 * @param fileNames
	 *            指定的多个文件名,按顺序对应
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public void work(List<String> fileNames) throws Exception {
		String errMess = "";

		// 上传文件,并解析出所有的表单字段，包括普通字段和文件字段
		List<FileItem> items = getFileItems(uploadSetting());
		for (int i = 0; i < items.size(); i++) {
			FileItem fileItem = items.get(i);
			if (!fileItem.isFormField()) {// 只对文件字段处理
				String fileName = fileNames.get(i);// 文件名和文件字段是一一对应的
				try {
					if (fileItem.getName() != null && fileItem.getSize() != 0) {
						if (checkFile(fileItem.getName()))
							writeFile(fileItem, fileName);
						else
							errMess += " [ " + fileName + " 文件格式错误,导致上传失败] ";
					} else
						errMess += " [ " + fileName + " 为空文件,导致上传失败] ";
				} catch (Exception e) {
					errMess += " [上传 " + fileName + " 失败] ";
					continue;
				}
			} else {
				storeFormFeild(fileItem);
			}
		}
		if (errMess.length() > 0) {
			throw new Exception(errMess);
		}
	}

	/**
	 * 将缓存中的文件写到网络磁盘中
	 * 
	 * @param fileItem
	 * @throws Exception
	 */
	private void writeFile(FileItem fileItem) throws Exception {
		String fileName = getRandomId();
		doWrite(fileItem, fileName);
	}

	/**
	 * 将缓存中的文件写到网络磁盘中,指定文件保存在网络磁盘中的文件名
	 * 
	 * @param fileItem
	 * @param fileName
	 *            指定的文件名,若为空则设随机设置文件名
	 * @throws Exception
	 */
	private void writeFile(FileItem fileItem, String fileName) throws Exception {
		doWrite(fileItem, fileName);
	}

	/**
	 * 执行写入操作
	 * 
	 * @param fileItem
	 * @param fileName
	 *            保存到磁盘中的文件名
	 * @throws Exception
	 */
	private void doWrite(FileItem fileItem, String fileName) throws Exception {
		try {
			String dir = request.getSession().getServletContext().getRealPath(
					"/");

			File dirFile = new File(dir + saveFileDir);
			if (!dirFile.exists()) {
				dirFile.mkdirs();
				if (!dirFile.canWrite()){
					//dirFile.setWritable(true);
				}
			}

			if (fileItem.getName().indexOf(".") > 0)
				fileName += fileItem.getName().substring(
						fileItem.getName().lastIndexOf("."),
						fileItem.getName().length());

			String newFile = dir + saveFileDir + fileName;

			File source = new File(newFile);
			if (!source.exists())
				source.createNewFile();

			// 保存文件，把缓存里的数据写到目标路径下
			fileItem.write(new File(newFile));

			if (Constants.IMG_MODE.equalsIgnoreCase(this.File_mode))
				// 图片模式需格式化图片尺寸
				ImgHelper.convrtImg(newFile, getBigWitdh(), getBigHeight());

			if (smallFlag) {// 判断是否需要生成缩略图
				String smallFile = dir + getSaveFileDir()
						+ Constants.THUMB_NAME + fileName;

				// 格式化缩略图图片尺寸
				ImgHelper.convrtImg(smallFile, getSmallWitdh(),
						getSamllHeight());
			}

			// 记录下保存的文件名
			savedFileName.add(fileName);
		} catch (Exception e) {
			throw new Exception(e);
		}
	}

	public void delUploadFile(String fileName) {
		String dir = request.getSession().getServletContext().getRealPath("/");
		File delFile = new File(dir + this.saveFileDir + fileName);

		delFile.delete();

		if (this.smallFlag) {
			File delSmallFile = new File(dir + this.saveFileDir
					+ Constants.THUMB_NAME + fileName);

			delSmallFile.delete();
		}

	}

	/**
	 * 获取以时间来生成的随机名称%{J-YYYYMMDDMinSS-随机数}
	 * 
	 * @return 自动生成的随机数
	 */
	private synchronized String getRandomId() {
		String ranCode = Constants.SUB_IMG;
		Random ran = new Random();
		int ranInt = ran.nextInt(100);
		Calendar now = Calendar.getInstance();

		ranCode += "-" + now.get(Calendar.YEAR) + ""
				+ (now.get(Calendar.MONTH) + 1) + "" + now.get(Calendar.DATE);
		ranCode += now.get(Calendar.HOUR_OF_DAY);
		ranCode += now.get(Calendar.MINUTE) + "" + now.get(Calendar.SECOND);
		ranCode += now.get(Calendar.MILLISECOND);
		ranCode += "-" + ranInt;

		return ranCode;
	}

	public List<String> getSavedFileName() {
		return savedFileName;
	}

	public boolean isSmallFlag() {
		return smallFlag;
	}

	public void setSmallFlag(boolean smallFlag) {
		this.smallFlag = smallFlag;
	}

	public int getSmallWitdh() {
		return SmallWitdh;
	}

	public void setSmallWitdh(int smallWitdh) {
		SmallWitdh = smallWitdh;
	}

	public int getSamllHeight() {
		return SamllHeight;
	}

	public void setSamllHeight(int samllHeight) {
		SamllHeight = samllHeight;
	}

	public int getBigWitdh() {
		return BigWitdh;
	}

	public void setBigWitdh(int bigWitdh) {
		BigWitdh = bigWitdh;
	}

	public int getBigHeight() {
		return BigHeight;
	}

	public void setBigHeight(int bigHeight) {
		BigHeight = bigHeight;
	}

	public String getSaveFileDir() {
		return saveFileDir;
	}

	public ProgressListener getPListener() {
		return pListener;
	}

	public void setPListener(ProgressListener listener) {
		pListener = listener;
	}

}
